#ifndef __VECTOR2D_H__
#define __VECTOR2D_H__

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include "Point2D.hpp"
#include "DivisionByZeroException.hpp"
#include <cmath>

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace Types {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

/// Vector 2D type
template <typename T>
class Vector2D : public Point2D<T> {

	public:
	
		/// Ccnstructor
		Vector2D() {}

		/// Constructor with initialisation values
		Vector2D(const T & x, const T & y) : Point2D<T>(x,y) {}

		/// Create a copy of the given instance
		template <typename T2>
		Vector2D(const Vector2D<T2> & inst)
		: Point2D<T>(inst) {}

		/// Affectation operator
		template <typename T2>
		Vector2D<T> & operator = (const Vector2D<T2> & inst) {
			if (this != &inst) {
				this->x	= static_cast<T>(inst.x);
				this->y = static_cast<T>(inst.y);
			}
			return *this;
		}

		/// Return the norm of the vector
		const double Norm() const {
			return std::sqrt(this->x*this->x + this->y*this->y);
		}
		
		/// Return the squared norm of the vector
		const double SquaredNorm() const {
			return this->x*this->x + this->y*this->y;
		}

		/// Normalise the vector
		void Normalise() {
			double norm = this->Norm();
			this->x = static_cast<T>((static_cast<double>(this->x))/norm);
			this->y = static_cast<T>((static_cast<double>(this->y))/norm);
		}
		
		/// Return the normalised vector
		const Vector2D<T> GetNormalised() const {
			const double norm = this->Norm();
			return Vector2D<T>(	static_cast<T>((static_cast<double>(this->x))/norm), static_cast<T>((static_cast<double>(this->y))/norm) );
		}

		/// Add two vectors
		const Vector2D<T> operator+ (const Vector2D<T> & v) const {
			return Vector2D<T> ( this->x + v.x, this->y + v.y);
		}

		/// Subtract two vectors
		const Vector2D<T> operator- (const Vector2D<T> & v) const {
			return Vector2D<T> ( this->x - v.x, this->y - v.y);
		}
		
		/// Negate
		const Vector2D<T> operator- () const {
			return Vector2D<T> ( -this->x, -this->y );
		}

		/// Dot product
		const float operator^ (const Vector2D<T> & v) const {
			return this->x * v.x + this->y * v.y;
		}
	
		/// Scale multiplying
		const Vector2D<T> operator* (const float s) const {
			return Vector2D<T> ( this->x * s, this->y * s );
		}
		
		/// Scale multiplying
		void operator*= (const float s) {
			this->x *= s; this->y *= s;
		}
		
		/// Scale multiplying
		friend const Vector2D<T> operator* (const float s, const Vector2D<T> & v) {
			return Vector2D<T> ( v.x * s, v.y * s );
		}
		
		/// Scale dividing
		const Vector2D<T> operator/ (const float s) const {
			if (s == 0.f)
				throw Exceptions::DivisionByZeroException("Vector = Vector / 0 is not allowed");
			return Vector2D<T> ( this->x / s, this->y / s );
		}
		
		/// Scale dividing
		void operator/= (const float s) {
			if (s == 0.f)
				throw Exceptions::DivisionByZeroException("Vector /= 0 is not allowed");
			this->x /= s; this->y /= s;
		}

		/// Return the vector's line's normal vector
		const Vector2D<T> GetNormal() const {
			return Vector2D<T>(-this->y, this->x);
		}

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#else

namespace Types {
	template <typename T> class Vector2D;
}

#endif
